import socket
import time
import json
import struct

#本demo设备地址固定为1

def calculate_crc16(data):
    crc = 0xFFFF
    for byte in data:
        crc ^= byte
        for _ in range(8):
            if crc & 0x0001:
                crc = (crc >> 1) ^ 0xA001
            else:
                crc >>= 1
    return crc

def verify_response(response):
    if response is None:
        return False

    # 只是简单判断下应答内容是否为正常
    if len(response) != 12 or response[0] != 0xAA or response[-3] != 0:
        print("应答错误")
        return False

    data_to_check = response[4:-2]
    received_crc = (response[-1] << 8) | response[-2]
    calculated_crc = calculate_crc16(data_to_check)

    return received_crc == calculated_crc
    
class UpgradeClient:
    def __init__(self, host, port):
        self.host = host
        self.port = port
        self.sock = None

    def connect(self):
        try:
            self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.sock.connect((self.host, self.port))
            print("成功连接到服务器")
            return True
        except Exception as e:
            print(f"连接失败: {e}")
            return False

    def disconnect(self):
        if self.sock:
            self.sock.close()
            self.sock = None
            # print("连接已断开")

    def send_command(self, command):
        try:
            # 发送命令
            self.sock.send(command)
            # 等待响应
            response = self.sock.recv(1024)
            # print(f"发送命令: {command}")
            # print(f"收到响应: {response}")
            return response
        except Exception as e:
            print(f"发送命令失败: {e}")
            return None

def read_binary_file_enhanced(client, filename):
    total_bytes = 0
    chunk_count = 0
    
    with open(filename, 'rb') as f:
        # 获取文件总大小
        f.seek(0, 2)  # 移到文件末尾
        file_size = f.tell()  # 获取文件大小
        f.seek(0)  # 回到文件开头
        
        print(f"文件大小: {file_size} 字节")
        print("开始读取...\n")
        
        while True:
            chunk = f.read(100)  #一次发送必须小于等于100个字节 且是4的倍数
            if not chunk:
                break
                
            chunk_count += 1
            chunk_size = len(chunk)

            
            #拼接 prefix + dev_addr + func + len + data + crc16
            #其中data前四个字节是烧录的地址也就是偏移量
            data_offset = struct.pack('<I', total_bytes) #小端模式的memcpy
            #放在后面
            total_bytes += chunk_size
            crc_data = b'\x01\x02\x02\x00' + bytes([chunk_size + 4]) + data_offset + chunk
            crc_value = calculate_crc16(crc_data)
            
            ota_data = b'\xAA\x55\xAA\x55' + crc_data + bytes([crc_value & 0xFF]) + bytes([crc_value >> 8])
            print(ota_data.hex())
            response = client.send_command(ota_data)
            if not verify_response(response): #可以添加错误重传
                return
            
            print(f"第 {chunk_count} 块, 块大小: {chunk_size} 字节, 偏移地址为：{total_bytes}")
            print("-" * 50)
        
        time.sleep(0.1)
        # 发送ota状态指令
        ota_command = b'\xAA\x55\xAA\x55\x01\x02\x01\x00\x01\x00\x78\x66'
        response = client.send_command(ota_command)
        if not verify_response(response):
            return
        
        time.sleep(0.1)
        # 发送重启命令
        restart_command = b'\xAA\x55\xAA\x55\x01\x01\x04\x00\x00\x59\xFD'
        response = client.send_command(restart_command)
        if not verify_response(response):
            return
        print("\n更新完成:")
        print(f"总块数: {chunk_count}")

def main():
    # 创建客户端实例
    client = UpgradeClient("192.168.201.211", 4196)

    # 第一次连接
    if not client.connect():
        return

    # 发送ota状态指令
    ota_command = b'\xAA\x55\xAA\x55\x01\x02\x01\x00\x01\x01\xB9\xA6'
    response = client.send_command(ota_command)
    if not verify_response(response):
        client.disconnect()
        return
    time.sleep(0.1)

    # 发送重启命令
    restart_command = b'\xAA\x55\xAA\x55\x01\x01\x04\x00\x00\x59\xFD'
    response = client.send_command(restart_command)
    if not verify_response(response):
        client.disconnect()
        return

    print("重启命令已确认，等待设备重启...")
    client.disconnect()
    
    # 等待3秒
    print("等待3秒...")
    time.sleep(3)
    
    # 尝试重新连接
    retry_count = 0
    while retry_count < 5:  # 最多尝试5次重连
        if client.connect():
            break
        print("重连失败，1秒后重试...")
        time.sleep(1)
        retry_count += 1
    
    if retry_count == 5:
        print("重连失败次数过多，退出程序")
        client.disconnect()
        return

    read_binary_file_enhanced(client, "Relay_Ctl_App.bin")
    client.disconnect()
    
if __name__ == "__main__":
    # print(verify_response(b'\xAA\x55\xAA\x55\x01\x01\x04\x00\x01\x00\x3C\xAA'))
    # read_binary_file_enhanced("Relay_Ctl.bin")
    main()